﻿using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using RayDen.Library.Components.SystemComponents;
using RayDen.RayEngine.Engines.AO;
using RayDen.RayEngine.Engines.BDPT;
using RayDen.RayEngine.Engines.DirectLight;
using RayDen.RayEngine.Engines.LightTracer;
using RayDen.RayEngine.Engines.MCMC;
using RayDen.RayEngine.Engines.ManyLights;
using RayDen.RayEngine.Engines.PathTracer;
using RayDen.RayEngine.Engines.PathTracer.MIS;
using RayDen.RayEngine.Engines.PhotonMapping;
using RayDen.RayEngine.Engines.SpectralPT;
using RayDen.RayEngine.Engines.ProgressivePhotonMapping;

namespace RayDen.RayEngine
{

    [Serializable]
    [DataContract(Name = "RenderingEngines")]
    public enum RenderingEngines
    {
        [EnumMember]
        Direct,
        [EnumMember]
        PathTracer,
        [EnumMember]
        LightTracer,
        [EnumMember]
        VolumePathTracer,
        [EnumMember]
        SpectralPathTracer,
        [EnumMember]
        PhotonMapping,
        [EnumMember]
        AO,
        [EnumMember]
        SWPathTracer,
        [EnumMember]
        MCMCPathTracer,
        [EnumMember]
        MISPathTracer,
        [EnumMember]
        BDPT,
        [EnumMember]
        PPM,
        [EnumMember]
        MLPT,
        [EnumMember]
        PssMlt,
        [EnumMember]
        RayTracer,
        /// <summary>
        /// Noise-free path tracer
        /// </summary>
        [EnumMember]
        BiasedPathTracer,
        /// <summary>
        /// Metropolis Light Transport (Path Space)
        /// </summary>
        [EnumMember]
        Mlt,
        /// <summary>
        /// Adjoint Photon Tracer
        /// </summary>
        [EnumMember]
        Apt,

        [EnumMember]
        External,

        [EnumMember]
        VPT,
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    public class RenderingEngineAttribute : Attribute
    {
        public RenderingEngines EngineType { get; set; }
    }


    public class EngineFactory
    {
        public static BaseRenderingEngine CreateEngine(RenderingEngines type, RayEngineScene scene, bool lowLatency)
        {
            try
            {
                foreach (var definedType in AppDomain.CurrentDomain.GetAssemblies().SelectMany(pt => pt.DefinedTypes))
                {
                    if (Attribute.IsDefined(definedType, typeof(RenderingEngineAttribute)))
                    {
                        var attrVal =
                            Attribute.GetCustomAttribute(definedType, typeof(RenderingEngineAttribute)) as
                            RenderingEngineAttribute;
                        if (attrVal != null && attrVal.EngineType == type)
                        {
                            var re = Activator.CreateInstance(definedType, scene) as BaseRenderingEngine;
                            if (re != null)
                            {
                                re.LowLatencySampling = lowLatency;
                                return re;
                            }
                        }

                    }
                }
            }
            catch
            {
            }

            switch (type)
            {
                case RenderingEngines.Direct:
                    return new DirectLightEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.PPM:
                    return new ProgressivePhotonMappingEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.BDPT:
                    return new BDPTEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.PathTracer:
                    return new PathTracerEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.PhotonMapping:
                    return new PhotonMapRenderer(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.AO:
                    return new AmbientOcclusionEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.VolumePathTracer:
                    return new VolumePathTracerEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.SpectralPathTracer:
                    return new SpectralPathTracerEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.MISPathTracer:
                    return new MisPathTracerEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.MCMCPathTracer:
                    return new MCMCPathTracerEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.MLPT:
                    return new MLEngine(scene) { LowLatencySampling = lowLatency };
                case RenderingEngines.LightTracer:
                    return new LightTracerEngine(scene) { LowLatencySampling = lowLatency };
            }

            throw new NotImplementedException();
        }
    }
}
